package com.globalwave.datasync.engine;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.Timestamp;
import java.sql.Types;
import java.util.ArrayList;
import java.util.List;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.globalwave.datasync.ConvertorInf;
import com.globalwave.datasync.SyncConfig;
import com.globalwave.datasync.SyncContext;

public abstract class BaseSync {

	protected Connection srcConnection = null ;
	protected Connection destConnection = null ;

	protected SyncConfig config = null ;
	
	protected Log log = LogFactory.getLog(getClass());
	protected List<PreparedStatement> statementsToClose = new ArrayList<PreparedStatement>();
	protected SyncContext context = null ;
	
	public abstract void transfer() throws Exception ;
	
	final public void execute(SyncContext context) throws Exception {

        try {
        	this.context = context ;
        	transfer() ;
        } catch(SQLException ex) {
        	sqlExceptionHandle(ex) ;
            throw ex;
        } catch(Exception e) {
        	log.error("Exception: " + e.getMessage());
            throw e;
        } finally {
        	closeStatements() ;
        }
	}
    
    /**
	 * 使用SQL，获取数据集
	 * 
	 * @param conn 数据库连接
	 * @param querySQL 查询SQL
	 * @param parameters 查询参数
	 * @return 数据集
	 */
	protected ResultSet queryResultSet(Connection conn, String querySQL, Object[] parameters) throws Exception {
		return queryResultSet(conn, querySQL, parameters, false) ;
	}
	
	protected ResultSet queryResultSet(Connection conn, String querySQL, Object[] parameters, boolean isScroll) throws Exception {
	    
	    PreparedStatement ps = getPrepareStatement(conn, querySQL, isScroll);
		
		if (parameters != null) {
			for (int i = 0; i < parameters.length; i++) {
			    ps.setObject(i + 1, parameters[i]);
			}
		}
		
		return ps.executeQuery();
			
	}

	protected int execute(Connection conn, String sql, Object[] parameters) throws Exception {
	    
	    PreparedStatement ps = getPrepareStatement(conn, sql, false);
		
		if (parameters != null) {
			for (int i = 0; i < parameters.length; i++) {
			    ps.setObject(i + 1, parameters[i]);
			}
		}
		
		return ps.executeUpdate();
			
	}

	protected PreparedStatement getPrepareStatement(Connection conn, String sql) throws Exception{
		return getPrepareStatement(conn, sql, false) ;
	}
	
	protected PreparedStatement getPrepareStatement(Connection conn, String sql, boolean isScroll) throws Exception{

		if (log.isDebugEnabled()) {
			log.debug("sql - " + sql) ;
		}
	    PreparedStatement ps =
	    	isScroll?
	    			conn.prepareStatement(sql, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY)
	    			:conn.prepareStatement(sql);
	    statementsToClose.add(ps) ;
	    
	    return ps ;
	}

	protected void closeStatements() throws Exception {
	    for (int i = statementsToClose.size() - 1 ; i >= 0 ; i --) {
	        PreparedStatement ps = statementsToClose.get(i) ;
	        if (ps == null) {
	            continue ;
	        }

            try {
                ps.close();
            } catch (SQLException e1) {
    			throw new Exception(e1);
            }
	    }    
	} 

    private void sqlExceptionHandle(SQLException ex) {
    	log.error("SQLException: " + ex.getMessage());
    	log.error("SQLState: " + ex.getSQLState());
        log.error("Message: " + ex.getMessage());
        log.error("Vendor error code: " + ex.getErrorCode());
    	
    }
    
    protected void processUpdateCounts(int[] updateCounts, Object[] ids, String message) {
        for (int i=0; i<updateCounts.length; i++) {
            if (updateCounts[i] >= 0) {
                // Successfully executed; the number represents number of affected rows
            } else if (updateCounts[i] == Statement.SUCCESS_NO_INFO) {
                // Successfully executed; number of affected rows not available
            } else if (updateCounts[i] == Statement.EXECUTE_FAILED) {
                // Failed to execute
            	if (i < ids.length) {
            		log.error(String.format(message, ids[i])) ;
            	}
            }
        }
    }
    

    protected void copyOneRowData(
            ResultSet srcResultSet, 
            PreparedStatement stmt) 
    throws Exception {
        
    	//SyncColumn[] columns = config.getDestination().getSyncColumns() ;
    	
        ResultSetMetaData meta = srcResultSet.getMetaData() ;
        int relationLen = meta.getColumnCount() ;
        
        for (int i = relationLen ; i > 0 ; i --) {
            
            //Object value = getColValue(srcResultSet, i, true, isChineseColumn, isEncryptColumn) ;
            //stmt.setObject(i, value) ;

    		ConvertorInf c = getConvertor(i) ;
        	if (Types.TIMESTAMP == meta.getColumnType(i)) {
        		if (c == null) {
        		    stmt.setTimestamp(i, srcResultSet.getTimestamp(i)) ;
        		} else {
        		    stmt.setTimestamp(i, (Timestamp)c.valueOf(srcResultSet.getTimestamp(i))) ;
        		}
        	} else {
        		if (c == null) {
        		    stmt.setObject(i, srcResultSet.getObject(i)) ;
        		} else {
        			if (c.outputType() == Types.TIMESTAMP) {
        		        stmt.setTimestamp(i, (Timestamp)c.valueOf(srcResultSet.getObject(i))) ;
        			} else {
        				stmt.setObject(i, c.valueOf(srcResultSet.getObject(i))) ;
        			}
        		}
        	}
            //stmt.setObject(i, srcResultSet.getObject(i)) ;
        }
    }

    private ConvertorInf getConvertor(int i) {
		String convertor = null ;
		int index = i - 1 ;
		int colLen = config.getSource().getSyncColumns().length ;
		if (colLen > index) {
			convertor = config.getSource().getSyncColumns()[index].getConvertor() ;
		} else {
			convertor = config.getSource().getKeys()[index - colLen].getConvertor() ;
		}
    	if (convertor == null) {
    		return null ;
    	}
    	
    	try {
			return (ConvertorInf)Class.forName(convertor).newInstance() ;
		} catch (Exception e) {
			return null ;
		}
    }
}
